//============================================================================
// Name        : k-means.cpp
// Author      : Andrey Bicalho Santos and Clayson Sandro Celes
// Version     :
// Copyright   : Your copyright notice
// Description : k-means in C++, Ansi-style
//============================================================================

#include <cstdlib>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "ponto.h"
#include "centroide.h"

using namespace std;

//------------------------------------------------------------------------------
double DistanciaEuclidiana(float*v1,float*v2,int tamanho);//computa a Distancia Euclidiana entre dois vetores
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void Salvar(Ponto p[],Centroide c[],int numpontos,int dimensoes,int numcent);//salva o resultado num arquivo de texto
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
int CentroideStatus(Centroide c[],int dimensoes,int numcent,int limiar);//retorna se existe pelo menos um centroide sendo remanejado... retorna 0 caso nenhum centroide esteja mudando de posicao
//------------------------------------------------------------------------------
void InserePRotulos(Ponto p[],int numpontos);//insere rotulos em todos os pontos
int ContaPontos(int dimensoes);//conta quantos pontos existe no arquivo
void CarregaPontos(Ponto p[],int numpontos,int dimensoes);//carrega os pontos do arquivo.txt
void ShowPontos(Ponto p[],int numpontos);//mostra os pontos na tela
void InsertDim(Ponto p[],Centroide c[],int numpontos,int dimensoes,int numcent);//insere as dimensoes do ponto... coordenadas x,y,z,...,n
void DistribuiPontos(Ponto p[],int numpontos,int dimensoes,int grupos);//distribui os pontos aleatoriamente nos grupos
void ShowGrupo(Ponto p[],int numpontos,int dimensoes);//mostra o grupo dos pontos
void InsereNumCentroides(Ponto p[],int numpontos,int numcent);//insere, informa o numero de centroides existentes
//------------------------------------------------------------------------------
void IniciaCentroides(Centroide c[],Ponto p[],int numcent,int numpontos,int dimensoes);//inicia os centroides aleatoriamente entre os pontos existentes
void ShowCentroides(Centroide c[],int numcent,int dimensoes);//mostra as coordenadas dos centroides
void RotulaCentroides(Centroide c[],int numcent);//especifica qual centroide é dono de qual grupo
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void CalculaDistancia(Ponto p[],Centroide c[],int numpontos,int numcent,int dimensoes);//calcula a distancia de um ponto aos centroides
void MostraDistancias(Ponto p[],int numpontos,int numcent,int dimensoes);//mostra as distancias do ponto em relacao aos centroides
void ReagrupaPontos(Ponto p[],int numpontos,int numcent);//reagrupa os pontos conforme a menor distancia entre os centroides
void AtualizaCentroides(Ponto p[],Centroide c[],int numpontos,int numcent,int dimensoes);//atualiza a posicao dos centroides
//------------------------------------------------------------------------------

int main(int argc, char *argv[])
{
    int numpontos = 0,dimensoes = 0,numcent = 0;//numero de pontos, dimensoes do ponto, numero de centroides ou grupos respectivamente
    int limiar = 0,iteracao = 0,iteracoes = 0;

    cout << "Insira a quantidade de grupos: ";
    cin >> numcent;
    cout << endl << "Insira o numero de dimensoes dos pontos: ";
    cin >> dimensoes;
    cout << endl;
    cout << "Insira o Limiar de Repeticao: ";
    cin >> limiar;
    cout << endl;


    numpontos = ContaPontos(dimensoes);
    //cout << numpontos << endl; system("pause");

    Ponto *p;
    Centroide *c;

    p = new Ponto[numpontos];
    c = new Centroide[numcent];

    InsertDim(p,c,numpontos,dimensoes,numcent);
    CarregaPontos(p,numpontos,dimensoes);
    InsereNumCentroides(p,numpontos,numcent);
    ShowPontos(p,numpontos);
    DistribuiPontos(p,numpontos,dimensoes,numcent);
    ShowGrupo(p,numpontos,dimensoes);

    IniciaCentroides(c,p,numcent,numpontos,dimensoes);
    RotulaCentroides(c,numcent);
    ShowCentroides(c,numcent,dimensoes);
    system("pause");


    do{
       cout << endl << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
       cout << endl << endl << "Iteracao: " << iteracao << endl << endl;

       iteracoes = CentroideStatus(c,dimensoes,numcent,limiar);

       CalculaDistancia(p,c,numpontos,numcent,dimensoes);
       MostraDistancias(p,numpontos,numcent,dimensoes);

       ReagrupaPontos(p,numpontos,numcent);

       AtualizaCentroides(p,c,numpontos,numcent,dimensoes);
       ShowCentroides(c,numcent,dimensoes);

       ShowGrupo(p,numpontos,dimensoes);

       //cout << endl << endl;
       //system("pause");


       for(int k=0;k<numcent;k++)
        cout << endl << "Repeticoes do centroide " << k << ": " << c[k].GetStatus() << endl;



       iteracao++;
    }while(iteracoes == 1);

    Salvar(p,c,numpontos,dimensoes,numcent);



  cout << endl << endl;
  system("PAUSE");
  return 0;
}



//------------------------------------------------------------------------------
void CarregaPontos(Ponto p[],int numpontos,int dimensoes){
     FILE *arquivo;
     int rotulo = 0;

     if((arquivo = fopen("pontos.txt","r"))==NULL)
      cout << "Erro ao abrir arquivo!!" << endl;

    arquivo = fopen("pontos.txt","r");

   for(int k=0;k<numpontos;k++){
    for(int i=0;i<dimensoes;i++)
     fscanf(arquivo,"%f,",&p[k].coordenada[i]);

    fscanf(arquivo,"[%d]",&rotulo);
    p[k].SetRotulo(rotulo);
   }

   fclose(arquivo);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void ShowPontos(Ponto p[],int numpontos){
     cout << endl << "Pontos Existentes: " << endl;

      for(int i=0;i<numpontos;i++)
       p[i].MostraPonto();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void InsertDim(Ponto p[],Centroide c[],int numpontos,int dimensoes,int numcent){
     for(int i=0;i<numpontos;i++)
      p[i].InsereDim(dimensoes);

     for(int i=0;i<numcent;i++)
      c[i].InsereDim(dimensoes);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void DistribuiPontos(Ponto p[],int numpontos,int dimensoes,int grupos){
     for(int k=0;k<numpontos;k++){
      for(int i=0;i<dimensoes;i++)
       p[k].InsereGrupo(rand()%grupos);
     }
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void ShowGrupo(Ponto p[],int numpontos,int dimensoes){
    cout << endl << "Pontos e Grupos: " << endl;

     for(int k=0;k<numpontos;k++){
       for(int i=0;i<dimensoes;i++)
        cout << p[k].coordenada[i] << ",";

       p[k].MostraGrupo();
     }

}
//------------------------------------------------------------------------------
void InsereNumCentroides(Ponto p[],int numpontos,int numcent){
     for(int i=0;i<numpontos;i++)
      p[i].InsertNumCent(numcent);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void IniciaCentroides(Centroide c[],Ponto p[],int numcent,int numpontos,int dimensoes){
     int temp = 0;

     for(int k=0;k<numcent;k++){ temp = rand()%numpontos;
      for(int i=0;i<dimensoes;i++){
       c[k].coordenada[i] = p[temp].coordenada[i];
       c[k].coordanterior[i] = c[k].coordenada[i];
      }
     }
}
//------------------------------------------------------------------------------
void ShowCentroides(Centroide c[],int numcent,int dimensoes){
      cout << endl << "Centroides: " << endl;

     for(int k=0;k<numcent;k++){
       for(int i=0;i<dimensoes;i++)
        cout << c[k].coordenada[i] << ",";

       cout << c[k].RetornaOwnerGrup() << "\t\tcoordenada anterior: ";

       for(int n=0;n<dimensoes;n++)
        cout << c[k].coordanterior[n] << ",";

       cout << endl;

     }

}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void RotulaCentroides(Centroide c[],int numcent){
     for(int k=0;k<numcent;k++)
       c[k].InsertOwnerGrup(k);

}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void CalculaDistancia(Ponto p[],Centroide c[],int numpontos,int numcent,int dimensoes){
     for(int i=0;i<numpontos;i++){
       for(int k=0;k<numcent;k++){
             p[i].distancia[k] = DistanciaEuclidiana(p[i].coordenada,c[k].coordenada,dimensoes);
       }
     }
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void MostraDistancias(Ponto p[],int numpontos,int numcent,int dimensoes){
    cout << endl << "Distancias: " << endl;
    for(int i=0;i<numpontos;i++){
      cout << "Ponto: ";
      p[i].MostraPonto();
      p[i].MostraDistancias(numcent);
    }
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void ReagrupaPontos(Ponto p[],int numpontos,int numcent){
     int temp = 0;
     for(int i=0;i<numpontos;i++){
       temp = p[i].GetMenorDistancia(numcent);
       p[i].InsereGrupo(temp);
     }
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void AtualizaCentroides(Ponto p[],Centroide c[],int numpontos,int numcent,int dimensoes){
     float sumcoordenadas[dimensoes],cont = 0;
     int contapontos = 0;
     for(int i=0;i<dimensoes;i++)
      sumcoordenadas[i] = 0;

    for(int k=0;k<numcent;k++)
     for(int cont=0;cont<dimensoes;cont++)
      c[k].coordanterior[cont] = c[k].coordenada[cont];

    for(int k=0;k<numcent;k++){
     for(int i=0;i<numpontos;i++)
      if(p[i].RetornaGrupo() == k){
        contapontos++;
        for(int cont=0;cont<dimensoes;cont++)
          sumcoordenadas[cont] += p[i].coordenada[cont];
      }
      if(contapontos > 0)
        for(int n=0;n<dimensoes;n++){
          sumcoordenadas[n] /= contapontos;
          c[k].coordenada[n] = sumcoordenadas[n];
        }
     for(int b=0;b<dimensoes;b++)
      sumcoordenadas[b] = 0;

     contapontos = 0;
    }

}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void Salvar(Ponto p[],Centroide c[],int numpontos,int dimensoes,int numcent){
     FILE *arquivo;
     float temp = 0;
     int rotulo = 0,grupo = 0;
     //salvando os pontos
     if((arquivo = fopen("resultado pontos.txt","w"))==NULL)
      cout << "Erro ao abrir arquivo!!" << endl;

     arquivo = fopen("resultado pontos.txt","wt");

     for(int i=0;i<numpontos;i++){
      for(int k=0;k<dimensoes;k++){
       temp = p[i].coordenada[k];
       fprintf(arquivo,"%f,",temp);
      }
      grupo = p[i].RetornaGrupo();
      rotulo = p[i].GetRotulo();
      fprintf(arquivo,"{%d},[%d]\n",grupo,rotulo);
     }

     fclose(arquivo);

     //salvando os centroides
     if((arquivo = fopen("resultado centroides.txt","w"))==NULL)
      cout << "Erro ao abrir arquivo!!" << endl;

     arquivo = fopen("resultado centroides.txt","wt");

     for(int i=0;i<numcent;i++){
      for(int k=0;k<dimensoes;k++){
       temp = c[i].coordenada[k];
       fprintf(arquivo,"%f,",temp);
      }
      grupo = c[i].RetornaOwnerGrup();
      fprintf(arquivo,"{%d}\n",grupo);
     }

     fclose(arquivo);

}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
int ContaPontos(int dimensoes){
    FILE *arquivo;
    int quantidadePontos = 0;
    char temp[dimensoes+100];

    if((arquivo = fopen("pontos.txt","r"))==NULL)
      cout << "Erro ao abrir arquivo!!" << endl;

    arquivo = fopen("pontos.txt","r");

    while(fscanf(arquivo,"%s",temp)!=EOF){
     quantidadePontos++;
    }

    fclose(arquivo);
    return quantidadePontos;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//computa a Distancia Euclidiana entre dois vetores
//------------------------------------------------------------------------------
double DistanciaEuclidiana(float*v1,float*v2,int tamanho){
    double DE = 0;
    for(int i=0;i<tamanho;i++){
      //DE += pow((double)(v1[i]-v2[i]),2.0);
      DE += (v1[i]-v2[i])*(v1[i]-v2[i]);
    }
    return sqrt(DE);
}
//------------------------------------------------------------------------------
int CentroideStatus(Centroide c[],int dimensoes,int numcent,int limiar){
    int repeticao = 0;
    int temp = 0;

    for(int k=0;k<numcent;k++)
     for(int i=0;i<dimensoes;i++){
      if(c[k].coordenada[i] == c[k].coordanterior[i]){
       repeticao++;
       c[k].SetStatusSoma(repeticao);
      }
      else
           for(int n=0;n<numcent;n++)
            c[n].SetStatus(0);
     }


     for(int k=0;k<numcent;k++)
      if(c[k].GetStatus() >= limiar)
       temp++;

     cout << "Grupos repetindo dentro do limiar: " << limiar << " = " << temp << endl;

     if(temp == numcent) //centroides nao estao sendo remanejados
      return 0;
     else return 1; // centroides ainda estao sendo remanejados

}
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
void InserePRotulos(Ponto p[],int numpontos){
     for(int i=0;i<numpontos;i++)
      p[i].SetRotulo(i);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
